package com.cardone.common.aspect;

import com.cardone.common.annotation.RunAction;
import com.cardone.common.annotation.support.AfterRunActionSupport;
import com.cardone.common.annotation.support.BeforeRunActionSupport;
import com.cardone.common.annotation.support.ErrorRunActionSupport;
import com.cardone.context.ContextHolder;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.support.TaskUtils;

/**
 * Created by c100000002 on 15-1-26.
 */
@Slf4j
public class CommonAspect {
    /**
     * 设置属性
     *
     * @param joinPoint 切片点
     */
    public Object runActionPoint(final ProceedingJoinPoint joinPoint) throws Throwable {
        final java.lang.reflect.Method method = ((org.aspectj.lang.reflect.MethodSignature) joinPoint.getSignature()).getMethod();

        RunAction runAction = AnnotationUtils.findAnnotation(method, RunAction.class);

        if (runAction == null) {
            return joinPoint.proceed();
        }

        try {
            if (ArrayUtils.isNotEmpty(runAction.beforeBeanId())) {
                taskExecutor.execute(TaskUtils.decorateTaskWithErrorHandler(() -> this.beforeRunActionPoint(runAction, joinPoint.getArgs()), null, true));
            }

            Object returnData = joinPoint.proceed();

            if (ArrayUtils.isNotEmpty(runAction.afterBeanId())) {
                taskExecutor.execute(TaskUtils.decorateTaskWithErrorHandler(() -> this.afterRunActionPoint(runAction, joinPoint.getArgs(), returnData), null, true));
            }

            return returnData;
        } catch (Exception e) {
            if (ArrayUtils.isNotEmpty(runAction.errorBeanId())) {
                taskExecutor.execute(TaskUtils.decorateTaskWithErrorHandler(() -> this.errorRunActionPoint(runAction, joinPoint.getArgs(), e), null, true));
            }

            throw e;
        }
    }

    @Setter
    private TaskExecutor taskExecutor;

    private void beforeRunActionPoint(RunAction runAction, Object[] args) {
        for (String beforeBeanId : runAction.beforeBeanId()) {
            BeforeRunActionSupport beforeRunAction = ContextHolder.getBean(BeforeRunActionSupport.class, beforeBeanId);

            if (beforeRunAction == null) {
                continue;
            }

            try {
                beforeRunAction.run(runAction, args);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    private void afterRunActionPoint(RunAction runAction, Object[] args, Object returnData) {
        for (String afterBeanId : runAction.afterBeanId()) {
            AfterRunActionSupport afterRunAction = ContextHolder.getBean(AfterRunActionSupport.class, afterBeanId);

            if (afterRunAction == null) {
                continue;
            }

            try {
                afterRunAction.run(runAction, args, returnData);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    private void errorRunActionPoint(RunAction runAction, Object[] args, Exception ex) {
        for (String errorBeanId : runAction.errorBeanId()) {
            ErrorRunActionSupport errorRunAction = ContextHolder.getBean(ErrorRunActionSupport.class, errorBeanId);

            if (errorRunAction == null) {
                continue;
            }

            try {
                errorRunAction.run(runAction, args, ex);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }
}
